package com.example.demo.aspectj;

import com.example.demo.util.IDGenerateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
@Component
@Aspect
@Order(111111)
public class LoggerAspectJ {
    /**
     * 通过自动装配 拿到request及response
     * 切点定义的不同，可能导致自动装配失败，这里定义required=false，忽略无法装配的情况
     */
    @Autowired(required=false)
    HttpServletRequest request;
    @Autowired(required=false)
    HttpServletResponse response;
    /** 切点 */
    @Pointcut("@within(org.springframework.web.bind.annotation.RestController) " +
            "|| @within(org.springframework.stereotype.Controller)")
    public void cbpcLoggerAspectJ(){}

    /**
     * 前置通知 记录开始时间
     * @param joinPoint 切点
     * @throws InterruptedException
     */
    @Before("cbpcLoggerAspectJ()")
    public void doBefore(JoinPoint joinPoint) throws InterruptedException{
        createTraceId();
    }
    /**
     * 后置通知 返回通知
     * @param res 响应内容
     */
    @AfterReturning(returning = "res", pointcut = "cbpcLoggerAspectJ()")
    public void doAfterReturning(Object res) throws Throwable {
        // 处理完请求，返回内容
        dealTraceId();
    }
    /**
     * 后置通知 记录用户的操作
     * @param joinPoint 切点
     */
    @After("cbpcLoggerAspectJ()")
    public void doAfter(JoinPoint joinPoint) {
        dealTraceId();
    }

    /**
     *  异常通知 即使出现错误，也不要丢了guid信息
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "cbpcLoggerAspectJ()", throwing = "e")
    public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        dealTraceId();
    }
    private String createTraceId(){
        // 依次从 request mdc 里取
        String traceId = MDC.get(LoggerConstant.TRACE_ID);
        if(request != null && StringUtils.isNotEmpty(request.getHeader(LoggerConstant.TRACE_ID))) {
            try {
                traceId = request.getHeader(LoggerConstant.TRACE_ID);
            }catch (Throwable e){
                // response 不可用 直接忽略
            }
        }
        // 无法读取有效的traceid重新生成
        if(StringUtils.isEmpty(traceId)){
            traceId = IDGenerateUtil.SnowFlakeUtil.getIdStr();
            log.info("生成新的traceId:{}",traceId);
        }
        // 设置回去
        MDC.put(LoggerConstant.TRACE_ID, traceId);
        return traceId;
    }
    public void dealTraceId(){
        // 依次从 request mdc 里取
        String traceId = createTraceId();
        if(response != null) {
            try {
                response.setHeader(LoggerConstant.TRACE_ID, traceId);
            }catch (Throwable e){
                // response 不可用 直接忽略
            }
        }
    }
}
